package net.toby200.canalstoppages.model;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Objects;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Cache;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnSave;
import com.googlecode.objectify.annotation.Parent;

import java.util.Date;
import java.util.logging.Logger;

import javax.jdo.annotations.Embedded;

@Entity
@Cache
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public final class Notice {

    private static final Logger LOG = Logger.getLogger(Notice.class.getName());

	// parent is only used so we can use transactions
	@Parent @JsonIgnore Key<ParentEntity> parent = ParentEntity.getKey();
	@Id private long id;
    private String url;
    @Index private String title;
	@Index private String waterway;
	private String fromDate;
	private String toDate;
	@Index private String type;
	private String description;
	private String locationDetail;	
	@Index @Embedded private Location startLoc;
	@Index @Embedded private Location endLoc;
    @Index private long lastUpdated;
	
	/**
	 * No-arg constructor for Objectify
	 */
	@SuppressWarnings("unused")
	private Notice() {} 
	
	public Notice(long id, String title, String waterway, String fromDate, String toDate, String type, String description,
			String locationDetail, Location startLoc, Location endLoc, String url) {
		super();
        this.id = id;
		this.title = title;
		this.waterway = waterway;
		this.fromDate = fromDate;
		this.toDate = toDate;
		this.type = type;
		this.description = description;
		this.locationDetail = locationDetail;
		this.startLoc = startLoc;
		this.endLoc = endLoc;
        this.url = url;
	}

    @OnSave
    private void onSave() {
        lastUpdated = System.currentTimeMillis();
    }

    public long getId() {
        return id;
    }

	public String getTitle() {
		return title;
	}

	public String getWaterway() {
		return waterway;
	}

	public String getFromDate() {
		return fromDate;
	}

	public String getToDate() {
		return toDate;
	}

	public String getType() {
		return type;
	}

	public String getDescription() {
		return description;
	}

	public String getLocationDetail() {
		return locationDetail;
	}

	public Location getStartLoc() {
		return startLoc;
	}

	public Location getEndLoc() {
		return endLoc;
	}

    public String getUrl() {
        return url;
    }

    public long getLastUpdated() {
        return lastUpdated;
    }

    @Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((description == null) ? 0 : description.hashCode());
		result = prime * result + ((endLoc == null) ? 0 : endLoc.hashCode());
		result = prime * result + ((fromDate == null) ? 0 : fromDate.hashCode());
		result = prime * result + ((locationDetail == null) ? 0 : locationDetail.hashCode());
		result = prime * result + ((startLoc == null) ? 0 : startLoc.hashCode());
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		result = prime * result + ((toDate == null) ? 0 : toDate.hashCode());
		result = prime * result + ((type == null) ? 0 : type.hashCode());
		result = prime * result + ((waterway == null) ? 0 : waterway.hashCode());
        result = prime * result + ((url == null) ? 0 : url.hashCode());
		return result;
	}

	@Override
    // compares all fields except lastUpdated
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Notice other = (Notice) obj;
        if (url == null) {
            if (other.url != null)
                return false;
        } else if (!url.equals(other.url)) {
            return false;
        }
		if (description == null) {
			if (other.description != null)
				return false;
		} else if (!description.equals(other.description)) {
            LOG.info("Description updated for " + title + " now=" + description + ", previous="+other.description);
            return false;
        }
		if (endLoc == null) {
			if (other.endLoc != null)
				return false;
		} else if (!endLoc.equals(other.endLoc))
			return false;
		if (fromDate == null) {
			if (other.fromDate != null)
				return false;
		} else if (!fromDate.equals(other.fromDate))
			return false;
		if (locationDetail == null) {
			if (other.locationDetail != null)
				return false;
		} else if (!locationDetail.equals(other.locationDetail)) {
            LOG.info("locationDetail updated for " + title + " now=" + locationDetail + ", previous=" + other.locationDetail);
            return false;
        }
		if (startLoc == null) {
			if (other.startLoc != null)
				return false;
		} else if (!startLoc.equals(other.startLoc))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		if (toDate == null) {
			if (other.toDate != null)
				return false;
		} else if (!toDate.equals(other.toDate)) {
            LOG.info("toDate updated for " + title + " now=" + toDate + ", previous=" + other.toDate);
            return false;
        }
		if (type == null) {
			if (other.type != null)
				return false;
		} else if (!type.equals(other.type))
			return false;
		if (waterway == null) {
			if (other.waterway != null)
				return false;
		} else if (!waterway.equals(other.waterway))
			return false;
		return true;
	}

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("id", id)
                .add("title", title)
                .add("waterway", waterway)
                .add("fromDate", fromDate)
                .add("toDate", toDate)
                .add("type", type)
                .add("description", description)
                .add("locationDetail", locationDetail)
                .add("url", url)
                .add("lastUpdated", new Date(lastUpdated)
                )
                .toString();
    }
}
